/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package bab;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * La classe fourt tout qu'y permet de représenté l'état d'un sac et de calculé
 * la borne maximum
 *
 * @author Luc DUZAN
 */
public class Ub {

	private final UbInfo[] vobj; //objet trié par ratio
	private final UbInfo[] objInfo; //objet indexé par leur indice

	private int profit; //profit du sacado
	private int rcap; //capacité résiduelle (combien le sacado peut contenir de plus)
	private int borneSup; //valeur de la borne sup arrrondie à l'entier inférieur
	private int borneInf; //valeur de la borne inf

	private int objectFractionne; //indice de l'objet fractionné par la borne
	private double fractionVal; //valeur de la fraction fractionné
	private List<Integer> borneInfSol; //solution de la borne inf
	private int borneInfWeight; //poid de la borne inf
	private final int capacity;
	private double minWeight; //le poid le plus petit de tout les objets

	/**
	 * 
	 * @param data instance du problème
	 */
	public Ub(Data data) {
		this(data.getProfits(), data.getWeights(), data.getCapacity());
	}
	/**
	 *
	 * @param profits tableau des valeurs des objects
	 * @param weights tableau des poids des objects
	 * @param capacity capacité du sac
	 */
	public Ub(final int[] profits, final int[] weights, final int capacity) {
		assert profits.length == weights.length;
		int nbObject = profits.length;

		vobj = new UbInfo[nbObject];
		objInfo = new UbInfo[nbObject];
		this.capacity = capacity;

		if(nbObject <= 0) {
			//cas ou nbobject est nulle
			return;
		}

		UbInfo ui;
		minWeight = weights[0];
		for (int i = 0; i < nbObject; i++) {
			ui = new UbInfo(i, profits[i], weights[i]);
			vobj[i] = objInfo[i] = ui;
			minWeight = Math.min(minWeight, weights[i]);
		}

		Arrays.sort(vobj, Collections.reverseOrder());

		profit = 0;
		rcap = capacity;
	}

	/**
	 * Permet de changer le statut d'un objet (dans le sac ou pas)
	 *
	 * @param idObj
	 * @param etatObject
	 */
	public void fix(int idObj, EtatObject etatObject) {
		final UbInfo obj = objInfo[idObj];

		//if (obj.isUsed() && obj.isInBag()) {
		if (obj.getEtat() == EtatObject.IN_BAG) {
			profit -= obj.getProfit();
			rcap += obj.getWeight();
		}

		//obj.setInBag(inBag);
		obj.setEtat(etatObject);

		//if (inBag !=null && inBag) {
		if (etatObject == EtatObject.IN_BAG) {
			profit += obj.getProfit();
			rcap -= obj.getWeight();
		}
	}

	/**
	 * Permet de recalculer la borne supérieur et ses propriétés (après des
	 * ou un appelle à fix)
	 */
	@SuppressWarnings("null")
	public void compute() {
		int i = 0;
		double lrcap = rcap;
		boolean cont = true;
		borneInfWeight = capacity - rcap;
		borneInfSol = getIdInBag();

		borneSup = profit;

		UbInfo ui = null;
		//pour construire la borne sup on prend on met un maximum d'objet
		//ayant un ratio le plus grand possible puis on fractionne le dernier qui ne peut rentrer completement
		while (i < vobj.length && cont) {
			ui = vobj[i];

			if (ui.getEtat() == EtatObject.FREE) {
				if (ui.getWeight() <= lrcap) {
					lrcap -= ui.getWeight();
					borneSup += ui.getProfit();
					borneInfWeight += ui.getWeight();
					borneInfSol.add(ui.getIdo());

					if (lrcap == 0) {
						cont = false;
					} else {
						i++;
					}
				} else {
					cont = false;
				}
			} else {
				i++;
			}
		}

		borneInf = borneSup;

		/*
		 if(i == vobj.length) {
		 } else if(lrcap == 0) {
		 } else {*/
		if (!(i == vobj.length || lrcap == 0)) {
			objectFractionne = i;
			fractionVal = ((double) lrcap) / ((double) ui.getWeight());
			borneSup += Math.floor(fractionVal * ui.getProfit());
			i++;
			UbInfo uiVal;
			while (i < vobj.length) {
				uiVal = vobj[i];


				//pour la borne inf et la solution, on prend la partie entiere de la solution de la borne sup
				// et on essaie de faire rentrer encore qu'elle objet
				if (uiVal.getEtat() == EtatObject.FREE && uiVal.getWeight() <= lrcap) {
					lrcap -= uiVal.getWeight();
					if (lrcap < minWeight) {
						break;
					}
					borneInf += uiVal.getProfit();
					borneInfWeight += uiVal.getWeight();
					borneInfSol.add(uiVal.getIdo());
				}

				i++;
			}

		}

		assert borneInf <= borneSup;
	}

	/**
	 * 
	 * @return valeur de la solution qui vontient uniquemement les noeuds sur lequel on a branché
	 */
	public int getProfit() {
		return profit;
	}

	/**
	 * 
	 * @return le poids qu'on peut encore mettre dans le sac à dos
	 */
	public int getRcap() {
		return rcap;
	}

	/**
	 * 
	 * @return true ssi la borne sup et la même que la borne inf est qu'elle a donc une solution
	 */
	public boolean borneAUneSolution() {
		return borneInf == borneSup;
	}

	/**
	 * 
	 * @return la solution correspondant à la borne inférieur
	 */
	public List<Integer> getBorneInfSol() {
		return borneInfSol;
	}

	/**
	 * 
	 * @return le poid de la solution de la borne inférieur
	 */
	public int getBorneInfWeight() {
		return borneInfWeight;
	}

	/**
	 * 
	 * @return Valeur de la borne supérieur
	 */
	public int getBorneSup() {
		return borneSup;
	}

	/**
	 * 
	 * @return id de l'object fractionné par la sol de la borne max
	 */
	public int getObjectFractionne() {
		return objectFractionne;
	}

	public double getFractionVal() {
		return fractionVal;
	}

	/**
	 * 
	 * @return les id des objets qui sont dans le sac
	 */
	public List<Integer> getIdInBag() {
		List<Integer> result = new LinkedList<>();
		for (UbInfo ui : objInfo) {

			//if(ui.isUsed() && ui.isInBag()) {
			if (ui.getEtat() == EtatObject.IN_BAG) {
				result.add(ui.getIdo());
			}
		}

		return result;
	}

	public int getBorneInf() {
		return borneInf;
	}

	public UbInfo[] getVobj() {
		return vobj;
	}

	public UbInfo[] getObjInfo() {
		return objInfo;
	}

}
